#include "AccessoryLib.h"
#include "Log.h"
#include <unistd.h>
#include <pthread.h>
#include <sys/system_properties.h>

AccessoryLib* AccessoryLib::pInstance = NULL;

struct libusb_device_handle  * deviceHandler = NULL;
libusb_context* context = NULL;

int libusbHaveInterruptEndpoint = 0 ; //是否有中断端点
int mAccessoryLibInit = -1; //配件库是否初始化成功
volatile int keepLibusbRunning = 1;

uint8_t  bEndpointAddressInterrupt = 0x00; //中断端点地址
uint8_t  bEndpointAddressIn = 0x00; //bulk in 读数据端点地址
uint8_t  bEndpointAddressOut = 0x00; //bulk out 写数据端点地址
/** Number of this interface */
uint8_t  bInterfaceNumber = 0x00;

static unsigned char iAP2CheckBuf[]       = { 0xFF, 0x55, 0x02, 0x00, 0xEE, 0x10 };
/** USB-IF vendor ID */
uint16_t idVendor = 0x0000;

/** USB-IF product ID */
uint16_t idProduct = 0x0000;

// a flag set when a SIGINT has been received
volatile bool g_signaled = false;

void (*pFuncCarPlayDevicePlugin)( );

void printBuffer(unsigned char buf[], int length) {
//*
	char *p = (char*) malloc(length * sizeof(char)*10);
	int offset = 0;
	memset(p, 0, length * 10);
	for (int i = 0; i <length; i++) {
		offset += sprintf(p + offset, " %02X", (unsigned char) buf[i]);
	}
	LOGD("buf:: string=%s", p);
	free(p);
}

AccessoryLib* AccessoryLib::getInstance() {
	if (NULL == pInstance) {
	if (NULL == pInstance) {
		pInstance = new AccessoryLib();
		LOGD("pInstance=new AccessoryLib()");
	}
	}
	return pInstance;
}

AccessoryLib::AccessoryLib() {
	LOGD("AccessoryLib::AccessoryLib");
}

AccessoryLib::~AccessoryLib() {
	LOGD("AccessoryLib::~AccessoryLib");
    if( deviceHandler ) libusb_close(deviceHandler);
    deviceHandler = NULL;
    libusb_exit( context );
}

int AccessoryLib::accessoryLibUninit() {
	LOGD("AccessoryLib::accessoryLibUninit \n");

	if( mAccessoryLibInit != -1)
	{
		keepLibusbRunning = 0;
	    if( deviceHandler ) libusb_release_interface(deviceHandler,bInterfaceNumber);
	    if( deviceHandler ) libusb_close(deviceHandler);
	    deviceHandler = NULL;
	    // 释放libusb 的资源
	    libusbHaveInterruptEndpoint = 0;

	    mAccessoryLibInit = -1;
	    idVendor = 0x00;
	    idProduct = 0x00;
	    bEndpointAddressIn = 0x00;
	    bEndpointAddressOut = 0x00;
	    bEndpointAddressInterrupt = 0x00;
	    bInterfaceNumber = 0x00;
	    LOGD("AccessoryLib::accessoryLibUninit success. \n");
	}
	return 0;

}
pthread_t 		m_libusbObserverTid;
pthread_t *		m_libusbObserverTidPtr;

pthread_t 		m_libusbReadTid;
pthread_t *		m_libusbReadTidPtr;

void *libusbReadThread(void *arg) {
	while( libusbHaveInterruptEndpoint )
	{
		    int retLen = 0;
		    unsigned char read_buf[ 64 ] = { 0 };
			libusb_interrupt_transfer(deviceHandler,
		    		(unsigned char)bEndpointAddressInterrupt,
					read_buf,
					64,
		    		&retLen,
					0);
//			LOGD("libusbReadThread...retLen = %d  \n" ,retLen);
//			printBuffer(read_buf, 64);
			usleep(20);
	}
	return (NULL);
}

/* Called when a SIGINT is received after libusb has been set up */
void sigHandler(int signum) {
	LOGD("SIGINT received!");
	g_signaled = true;
	return;
}

static int hotplug_callback(
		struct libusb_context* ctx,
		struct libusb_device* device,
		libusb_hotplug_event event,
		void* user_data) {
	LOGD("Tinker hotplug_callback!");
	if(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED == event &&  device != nullptr )
	{
	    struct libusb_device_descriptor desc;
	    memset(&desc,0,sizeof(struct libusb_device_descriptor));
	    libusb_get_device_descriptor(device,&desc);
	    LOGD("Tinker hotplug_callback => vid:0x%04x pid:0x%04x", desc.idVendor, desc.idProduct);
	    uint8_t bNumConfigurations= desc.bNumConfigurations;
	    uint8_t CarPlayDeviceFound = 0;
		for(int j = 0; j < bNumConfigurations ; j++ ){
			libusb_config_descriptor *config_descriptor;
			libusb_get_config_descriptor(device, j, &config_descriptor);
			//判读是否是CarPlay车机 iap2 interface
			//在协议文档的 396 页 ， 526页是iap2
			uint8_t bNumInterfaces = config_descriptor->bNumInterfaces;
			uint8_t usbFound = 0;
			LOGD("Tinker hotplug_callback => vid:0x%04x pid:0x%04x bNumInterfaces = %d \n ", desc.idVendor, desc.idProduct,bNumInterfaces);
			for(int ii = 0; ii < bNumInterfaces; ii++ )
			{
				struct libusb_interface interface = config_descriptor->interface[ii];
	    		int num_altsetting = interface.num_altsetting;
	    		LOGD("Tinker hotplug_callback => vid:0x%04x pid:0x%04x num_altsetting = %d \n ", desc.idVendor, desc.idProduct,num_altsetting);
	    		for(int z = 0; z < num_altsetting; z++)
	    		{
	    			uint8_t  bInterfaceClass = interface.altsetting[z].bInterfaceClass;
	    			uint8_t  bInterfaceSubClass = interface.altsetting[z].bInterfaceSubClass;
	    			uint8_t  bInterfaceProtocol = interface.altsetting[z].bInterfaceProtocol;
	    			uint8_t  bNumEndpoints = interface.altsetting[z].bNumEndpoints;

	    			LOGD("Tinker hotplug_callback => bInterfaceClass:%02x bInterfaceSubClass:%02x bInterfaceProtocol:%02x bNumEndpoints:%d \n ", bInterfaceClass, bInterfaceSubClass,bInterfaceProtocol,bNumEndpoints);

	        		if( (bInterfaceClass == 0xFF
	        				&& bInterfaceSubClass == 0xF0
	    					&&bInterfaceProtocol==0x00 )
	        				&& ( bNumEndpoints > 0 )
	        				){
	        			LOGD("The accessory vid:0x%04x pid:0x%04x", desc.idVendor, desc.idProduct);
	        			idVendor = desc.idVendor;
	        			idProduct = desc.idProduct;
	        			struct libusb_interface_descriptor altsetting = interface.altsetting[z];
	        			const struct libusb_endpoint_descriptor * endpointArrays= altsetting.endpoint;
	        			int endpoints = altsetting.bNumEndpoints;
	        			bInterfaceNumber = altsetting.bInterfaceNumber;
	        			LOGD("Usb device endpoints cnt %d ",endpoints);
	        			for(int i=0; i<endpoints; i++)
	        			{
	        				struct libusb_endpoint_descriptor ep=endpointArrays[i];
	        				// libusb_endpoint_direction
	        				LOGD("Usb device bEndpointAddress  %02x ",ep.bEndpointAddress);
	        				uint8_t  epAttributes = ep.bmAttributes & 0x03;
	        				LOGD("Usb device epAttributes  %d ",epAttributes);
	        				// Bits 0:3 are the endpoint number
	        				uint8_t epNumber = ep.bEndpointAddress & 0x0F;
	        				uint8_t direction=( ep.bEndpointAddress & 0x80 ) >> 7;
	        				LOGD("Usb device endpoints direction %d ",direction);
	        				if( direction == 1 && epAttributes == 2 )
	        				{
	        					//bulkIn:read
	        					bEndpointAddressIn = ep.bEndpointAddress;
	        					LOGD("Usb device read endpoints address %d ",bEndpointAddressIn);
	        				}else if(direction == 0 && epAttributes == 2)
	        				{
	        					//bulkOut=wirte
	        					bEndpointAddressOut = ep.bEndpointAddress;
	        					LOGD("Usb device write endpoints address %d ",bEndpointAddressOut);
	        				}else if ( direction == 1 && epAttributes == 3  )
	        				{
	        					//interrupt:read
	        					bEndpointAddressInterrupt = ep.bEndpointAddress;
	        					libusbHaveInterruptEndpoint = 1;
	        					LOGD("Usb device interrupt endpoints address %d ",bEndpointAddressInterrupt);
	        				}
	        			}
	        			usbFound = 1;
	        			break;
	        		}
	    		}

				if( usbFound == 1 )
				{
					break;
				}
			}
			libusb_free_config_descriptor(config_descriptor);
			if( usbFound == 1 )
			{
				CarPlayDeviceFound = 1;
				LOGD("Tinker hotplug_callback,CarPlay USB Found!");
				break;
			}
		}

		if( CarPlayDeviceFound == 1 && pFuncCarPlayDevicePlugin != nullptr )
		{
			LOGD("Tinker hotplug_callback,CarPlay USB Found,wait 100ms !");
			usleep( 100 * 1000 );
			pFuncCarPlayDevicePlugin();
		}
	}
	else if(LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT == event)
	{
		LOGD("Tinker hotplug_callback,LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT");
	}
	return 0;
}


void *libusbObserverThread(void *arg) {
	keepLibusbRunning = 1;
	char board_platform[ 92 ] = { 0 };
	__system_property_get("ro.board.platform", board_platform);
	while ( keepLibusbRunning ){
		LOGD("libusb: keep going.\n");
#if 1
		char path [ 256 ];
		memset( path, 0 , 256 );
		if (strcmp(board_platform, "msm8953") == 0) {
			//sd450
			strcpy(path, "/sys/devices/platform/soc/7000000.ssusb/mode");
		}else if (strcmp(board_platform, "mt8163") == 0) {
			//mtk mt8163
			strcpy(path, "/sys/devices/platform/mt_usb/usb_mode");
		}else if (strcmp(board_platform, "lahaina") == 0) {
			//qualcomm 6490
			strcpy(path, "/sys/devices/platform/soc/a600000.ssusb/mode");
		}else{
			strcpy(path, "/sys/devices/platform/soc/4e00000.ssusb/mode");
		}

		char otg_mode[256];
		FILE *fp_otg = fopen(path, "r");
		if (fp_otg != NULL)
		{
			while (fgets(otg_mode,sizeof(otg_mode),fp_otg))
			{
				otg_mode[255]='\0';		// just to be safe
				LOGD( "182 AccessoryLib get otg_mode = %s  \n" , otg_mode);
				usleep(10*1000);
				if(strstr(otg_mode ,"host" ) != NULL )
				{
					LOGD( "AccessoryLib is host otg_mode = %s  \n" , otg_mode);
					break;
				}
			}
			int fpotg_closed = fclose(fp_otg);
		}
#endif

		///>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
		int vendor_id = LIBUSB_HOTPLUG_MATCH_ANY;
		int product_id = LIBUSB_HOTPLUG_MATCH_ANY;
		int device_class = LIBUSB_HOTPLUG_MATCH_ANY;
		libusb_hotplug_event any_event = static_cast<libusb_hotplug_event>
			(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT);
		int ret = libusb_init(&context);
		if (ret != LIBUSB_SUCCESS) {
			LOGE("Failed to initialize libusb");
			return (NULL);
		}
		if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
			LOGE("Hotplug not supported by this build of libusb");
			libusb_exit(NULL);
			return (NULL);
		}
		libusb_hotplug_callback_handle handle;
		int rca = libusb_hotplug_register_callback(context, any_event,
				static_cast<libusb_hotplug_flag>(1), vendor_id, product_id, device_class,
				hotplug_callback, NULL, &handle);
		if (rca != LIBUSB_SUCCESS) {
			LOGE("Error creating hotplug callback");
			return (NULL);
		}
		signal(SIGINT, sigHandler);
		while (!g_signaled) {
			libusb_handle_events(context);
		}
		libusb_hotplug_deregister_callback(context, handle);
		libusb_exit(context);
		LOGD("Exited successfully.");
		///<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

	}
	return (NULL);
}

int AccessoryLib::accessoryLibCreate(void (*pFunc)( )) {
	pFuncCarPlayDevicePlugin = pFunc;
	int ret;
	if( m_libusbObserverTidPtr == NULL )
	{
		if ((ret = pthread_create(&m_libusbObserverTid, NULL, libusbObserverThread, NULL))
				!= 0) {
			LOGD("observe libusb thread create failed!\n");
		} else {
			m_libusbObserverTidPtr = &m_libusbObserverTid;
			LOGD("observe libusb thread create successed!\n");
		}
	}
	return 1;
}

int AccessoryLib::accessoryLibInit() {
    if( mAccessoryLibInit != -1 )
    {
    	LOGE("accessoryLibInit already init... \n" );
        return -1;
    }
	LOGD("The accessory vid:0x%04x pid:0x%04x", idVendor, idProduct);

	//尝试打开设备
	int retry = 0;
    while ( !(deviceHandler = libusb_open_device_with_vid_pid(context, idVendor, idProduct))   ){
    	LOGD("Usb device open fail...retry.");
    	++retry;
    	if( retry > 5)
    	{
    		break;
    	}
    	usleep(100*1000);
    }
    if( !deviceHandler )
    {
    	LOGD("Usb device open fail,and return...");
    	 libusb_exit(context);
    	return -1;
    }
	LOGD("Enumerate Usb device and prepare claim interface");
	int claimSuccess = libusb_claim_interface (deviceHandler,bInterfaceNumber);
	LOGD("Enumerate Usb device and prepare claim interface claimSuccess: %d",claimSuccess);

    mAccessoryLibInit = 0;
    LOGD("accessoryLibInit init completed... \n" );

    //另外起个线程读数据
    if( libusbHaveInterruptEndpoint == 1 )
    {
    	int ret;
    	if ((ret = pthread_create(&m_libusbReadTid, NULL, libusbReadThread, NULL))
    			!= 0) {
    		LOGD("observe libusb read thread create failed!\n");
    	} else {
    		m_libusbReadTidPtr = &m_libusbReadTid;
    		LOGD("observe libusb read thread create successed!\n");
    	}
    }
    return 0;
}
void AccessoryLib::accessoryLibDestory() {
	delete pInstance;
	pInstance = NULL;
}
int AccessoryLib::accessoryWrite(uint8 *data, uint16 len,int * length) {
	if(deviceHandler!= NULL){
        libusb_bulk_transfer(deviceHandler,
            		(unsigned char)bEndpointAddressOut,
					data,
    				len,
            		length,
    				0);
        return (*length);
	}

	return -1;
}
int AccessoryLib::accessoryRead(uint8 *data, uint16 len,int * length) {
	if(deviceHandler != NULL){
	    libusb_bulk_transfer(deviceHandler,
	    		(unsigned char)bEndpointAddressIn,
				data,
				len,
	    		length,
				0);
	    return (*length);
	}
	return -1;
}

int AccessoryLib::accessoryUsbVidPid(uint16 *vid, uint16 *pid,int * length) {
	if(deviceHandler != NULL){
//		LOGD("accessoryUsbVidPid vid:0x%04x pid:0x%04x", idVendor,idProduct);
		if( vid != NULL && pid != NULL)
		{
			*vid = idVendor;
			*pid = idProduct;
			 return (*length);
		}
	}
	return -1;
}



